package fr.asip.cps3.exemple.modele;

import iaik.pkcs.pkcs11.wrapper.CK_INFO;
import iaik.pkcs.pkcs11.wrapper.PKCS11;
import iaik.pkcs.pkcs11.wrapper.PKCS11Connector;
import iaik.pkcs.pkcs11.wrapper.PKCS11Constants;
import iaik.pkcs.pkcs11.wrapper.PKCS11Exception;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Iterator;
import java.util.Properties;
import java.util.TreeMap;
import java.util.Vector;

import javax.swing.JMenuItem;
import javax.swing.event.EventListenerList;
import javax.swing.filechooser.FileFilter;

import org.apache.log4j.Logger;

import fr.asip.cps3.exemple.modele.ecouteurs.ApplicationEcouteur;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionLibrairie;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionObjet;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionProgrammeExemple;
import fr.asip.cps3.exemple.modele.exceptions.ExceptionVue;
import fr.asip.cps3.exemple.modele.jni.ConnecteurJNI;
import fr.asip.cps3.exemple.modele.jni.TraitementsSpecifiques;
import fr.asip.cps3.exemple.modele.objets.Algo;
import fr.asip.cps3.exemple.modele.objets.Cle;
import fr.asip.cps3.exemple.modele.objets.Objet;
import fr.asip.cps3.exemple.modele.objets.Session;
import fr.asip.cps3.exemple.modele.objets.Slot;
import fr.asip.cps3.exemple.modele.objets.Token;
import fr.asip.cps3.exemple.modele.resultats.Resultat;
import fr.asip.cps3.exemple.modele.traitements.util.TraitementsUtil;
import fr.asip.cps3.exemple.vues.menus.ItemMenu;
import fr.asip.cps3.exemple.vues.menus.Menu;

/**
 * L'application
 *
 */
public class Application {

	/**
	 * Le loggeur
	 */
	 private static Logger log = Logger.getLogger(Application.class);			
	
	/**
	 * Fichier properties de l'application
	 */
	private Properties fichierProperties;
	
	/**
	 * Menu de l'application
	 */
	private MenuApplication menu;
	
	/**
	 * Librairie PKCS#11 utilisée
	 */
	private PKCS11 librairieP11;
	
	/**
	 * Pont JNI pour les traitements specifiques
	 */
	private TraitementsSpecifiques traitementsSpecifiques;

	/**
	 * Chemin de la librairie PKCS#11 utilisée
	 */
	private String cheminLibrairieP11;
	
	/**
	 * Chemin de la librairie Traitements spécifique utilisée
	 */
	private String cheminLibrairieTS;
	
	/**
	 * Nom de la librairie PKCS#11 utilisée
	 */
	private CK_INFO infoLibrairieP11;
	
	/**
	 * Liste d'écouteurs à notifier en cas de modification
	 */
	private EventListenerList ecouteurs;
	
	/**
	 * Liste de tous les slots de l'application
	 */
	private Vector<Slot> slots = new Vector<Slot>();

	/**
	 * Liste de tous les tokens de l'application
	 */
	private Vector<Token> tokens = new Vector<Token>();

	/**
	 * Map des algorithmes
	 */
	private TreeMap<String, Vector<Algo>> algos = new TreeMap<String, Vector<Algo>>(); 
	
	/**
	 * Map des sessions
	 */
	private TreeMap<String, Vector<Session>> sessions = new TreeMap<String, Vector<Session>>();
	
	/**
	 * Session en cours d'utilisation
	 */
	private Session sessionCourante;

	/**
	 * Map des clés publiques
	 */
	private TreeMap<String, Vector<Cle>> clesPubliques = new TreeMap<String, Vector<Cle>>(); 

	/**
	 * Map des clés privées
	 */
	private TreeMap<String, Vector<Cle>> clesPrivees = new TreeMap<String, Vector<Cle>>();

	/**
	 * Map des objets
	 */
	private TreeMap<String, Vector<Objet>> objets = new TreeMap<String, Vector<Objet>>();
	
	/**
	 * Vector des filtres d'objets
	 */
	private Vector<String> filtresObjets = new Vector<String>();
	
	/**
	 * Dernière signature de message
	 */
	private String signatureMessage;

	/**
	 * Dernière signature de fichier
	 */
	private String signatureFichier;

	/**
	 * Dernier message signé
	 */
	private char[] messageSigne;

	/**
	 * Dernier fichier signé
	 */
	private String fichierSigne;

	/**
	 * Constructeur avec argument
	 * @param cheminLibrairieP11 Chemin de la librairie PKCS#11 à utiliser
	 * @param cheminLibrairieTS Chemin de la librairie des Traitements spécifiques
	 * @throws IOException 
	 * @throws FileNotFoundException 
	 */
	public Application(String cheminLibrairieP11) throws FileNotFoundException, IOException {
		
		// chargement du fichier de properties
		fichierProperties = new Properties();
		fichierProperties.load(this.getClass().getResourceAsStream("/programmeExemple.properties"));
		
		menu = new MenuApplication();
		this.cheminLibrairieP11 = cheminLibrairieP11;
    	this.cheminLibrairieTS = fichierProperties.getProperty("cheminLibrairieTS."+getTypeOS());
		
		// Tentative de chargement de la librairie
		try {
			chargerLibrairie();
		} catch (Throwable e) {
			// Erreur gérée au niveau au dessus en testant si la librairie est nulle
			log.error(e.getLocalizedMessage());
		}
		
		if(librairieP11 != null) {
			
			chargerInfoLibrairie();
			ecouteurs = new EventListenerList();
		}
	}	
	
	/**
	 * Constructeur sans argument
	 * @throws IOException 
	 * @throws FileNotFoundException
	 */
	public Application() throws FileNotFoundException, IOException {
		
		// chargement du fichier de properties
		fichierProperties = new Properties();
		fichierProperties.load(this.getClass().getResourceAsStream("/programmeExemple.properties"));
	    
	    if(fichierProperties != null) {
	    
	    	this.cheminLibrairieP11 = fichierProperties.getProperty("cheminLibrairieP11."+getTypeOS());
	    	this.cheminLibrairieTS = fichierProperties.getProperty("cheminLibrairieTS."+getTypeOS());
		
			menu = new MenuApplication();
			
			// Tentative de chargement de la librairie
			try {
				chargerLibrairie();
			} catch (Throwable t) {
				// Erreur gérée au niveau au dessus en testant si la librairie est nulle
				log.error(t.getLocalizedMessage());
			}
			
			if(librairieP11 != null) {
				
				chargerInfoLibrairie();
				ecouteurs = new EventListenerList();
			}
	    }
	}

	/**
	 * Accesseur
	 * @return Fichier de properties
	 */
	public Properties getFichierProperties() {
		return fichierProperties;
	}

	/**
	 * Accesseur
	 * @return Menu de l'application
	 */
	public MenuApplication getMenu() {
		return menu;
	}

	/**
	 * Mutateur
	 * @param Menu de l'application
	 */
	public void setMenu(MenuApplication menu) {
		this.menu = menu;
	}
	
	/**
	 * Renvoie le sous menu ayant l'identifiant spécifié
	 * @param idMenu Identifiant du sous-menu
	 * @return Sous menu du menu de l'application ayant l'identifiant spécifié
	 */
	public Menu getMenu(String idMenu) {
		
		if(this.menu != null && this.menu.getItemsMenus() != null) {
			JMenuItem menu = this.menu.getItemsMenus().get(idMenu);
			return (menu instanceof Menu)?(Menu)menu:null;
		}
		return null;
		
	}

	/**
	 * Renvoie l'item de menu ayant l'identifiant spécifié
	 * @param idItemMenu Identifiant de l'item de menu
	 * @return L'item de menu ayant l'identifiant spécifié
	 */
	public ItemMenu getItemMenu(String idItemMenu) {
		
		if(this.menu != null && this.menu.getItemsMenus() != null) {		
			JMenuItem menu = this.menu.getItemsMenus().get(idItemMenu);
			return (menu instanceof ItemMenu)?(ItemMenu)menu:null;
		}
		return null;
		
	}
	
	/**
	 * Accesseur
	 * @return Chemin de la librairie PKCS#11 utilisée
	 */
	public String getCheminLibrairieP11() {
		return cheminLibrairieP11;
	}
	
	
	/**
	 * Accesseur
	 * @return Librairie PKCS#11 utilisée
	 */
	public PKCS11 getLibrairieP11() {
		return librairieP11;
	}

	/**
	 * Récupère le numéro de version
	 * @return Numéro de version du programme d'exemple
	 */
	public String getVersion() {
		return (this.fichierProperties != null && this.fichierProperties.getProperty("version") != null)?this.fichierProperties.getProperty("version"):"";
	}	
	
	/**
	 * Mutateur
	 * @param chemin Chemin de la librairie PKCS#11 à utiliser
	 */
	public void setLibrairieP11(String chemin) {
		
		try {
			this.cheminLibrairieP11 = chemin;
			chargerLibrairie();
		} catch (Throwable t){
			if(t instanceof ExceptionLibrairie)
				notifieProblemeMAJLibrairieP11(((ExceptionLibrairie)t).getCodeException());			
		}
		
		chargerInfoLibrairie();
		notifieMAJLibrairieP11();
		
		// Si jamais les traitements spécifiques sont à nouveau utilisable
		if(traitementsSpecifiques != null)
			getMenu().setItemMenuActif(MenuApplication.TRAITEMENTSSPECIFIQUES);
		
	}

	/**
	 * Accesseur
	 * @return Pont JNI pour les traitements specifiques
	 */
	public TraitementsSpecifiques getTraitementsSpecifiques() {
		return traitementsSpecifiques;
	}

	/**
	 * Accesseur
	 * @return Informations de la librairie PKCS#11
	 */
	public CK_INFO getInfoLibrairieP11() {
		return infoLibrairieP11;
	}

	/**
	 * Mutateur
	 * @param infoLibrairieP11 Informations de la librairie PKCS#11
	 */
	public void setInfoLibrairieP11(CK_INFO infoLibrairieP11) {
		this.infoLibrairieP11 = infoLibrairieP11;
	}

	/**
	 * Accesseur
	 * @return Tous les slots de l'application
	 */
	public Vector<Slot> getSlots() {
		return slots;
	}

	/**
	 * Accesseur
	 * @return Tous les tokens de l'application
	 */
	public Vector<Token> getTokens() {
		return tokens;
	}	

	/**
	 * Accesseur
	 * @return Tous les algorithmes de toutes les cartes
	 */
	public TreeMap<String, Vector<Algo>> getAlgo() {
		return algos;
	}

	/**
	 * Accesseur
	 * @return Toutes les sessions de l'application
	 */
	public TreeMap<String, Vector<Session>> getSessions() {
		return sessions;
	}

	/**
	 * Accesseur
	 * @return  La session courante
	 */
	public Session getSessionCourante() {
		return sessionCourante;
	}

	/**
	 * Mutateur
	 * @param session La session courante
	 */
	public void setSessionCourante(Session session) {
		
		sessionCourante = session;
		
		// MAJ menus
		declencheMAJMenus();
		
	}

	/**
	 * Accesseur
	 * @return Toutes les clés privées de l'application
	 */
	public TreeMap<String, Vector<Cle>> getClesPrivees() {
		return clesPrivees;
	}

	/**
	 * Accesseur
	 * @return Toutes les clés publiques de l'application
	 */
	public TreeMap<String, Vector<Cle>> getClesPubliques() {
		return clesPubliques;
	}

	/**
	 * Accesseur
	 * @return Tous les objets de l'application
	 */
	public TreeMap<String, Vector<Objet>> getObjets() {
		return objets;
	}

	/**
	 * Accesseur
	 * @return Tous les filtres des objets de l'application
	 */
	public Vector<String> getFiltresObjets() {
		return filtresObjets;
	}
	
	/**
	 * Ajout un filtre d'objet
	 * @param filtre Le filtre à ajouter
	 */
	public void ajouteFiltreObjet(String filtre) {
		if(!filtresObjets.contains(filtre))
			filtresObjets.add(filtre);
	}

	/**
	 * Supprime tous les filtres d'objets de l'application
	 * @return Tous les filtres des objets de l'application
	 */
	public void supprimeTousLesFiltresObjets() {
		filtresObjets.clear();
	}	
	
	/**
	 * Accesseur
	 * @return Dernière signature de message
	 */
	public String getSignatureMessage() {
		return signatureMessage;
	}

	/**
	 * Mutateur
	 * @param signatureMessage Dernière signature de message
	 */
	public void setSignatureMessage(String signatureMessage) {
		this.signatureMessage = signatureMessage;
	}

	/**
	 * Accesseur
	 * @return Dernière signature de fichier
	 */
	public String getSignatureFichier() {
		return signatureFichier;
	}

	/**
	 * Mutateur
	 * @param signatureFichier Dernière signature de fichier
	 */
	public void setSignatureFichier(String signatureFichier) {
		this.signatureFichier = signatureFichier;
	}

	/**
	 * Accesseur
	 * @return Dernier message signé
	 */
	public char[] getMessageSigne() {
		return messageSigne;
	}

	/**
	 * Mutateur
	 * @param messageSigne Dernier message signé
	 */
	public void setMessageSigne(char[] messageSigne) {
		this.messageSigne = messageSigne;
	}

	/**
	 * Accesseur
	 * @return Dernier fichier signé
	 */
	public String getFichierSigne() {
		return fichierSigne;
	}

	/**
	 * Mutateur
	 * @param fichierSigne Dernier fichier signé
	 */
	public void setFichierSigne(String fichierSigne) {
		this.fichierSigne = fichierSigne;
	}	
	
	/**
	 * Déclenche l'affichage d'un resultat
	 * @param resultat
	 * @throws ExceptionVue 
	 */
	public void declencheAffichageResultat(Resultat resultat) throws ExceptionVue {
		
		// MAJ menus
		//declencheMAJMenus();
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.afficheResultat(resultat);
			}
		}

	}

	/**
	 * Déclenche l'affichage d'un écran
	 * @param resultat
	 * @throws ExceptionVue 
	 */
	public void declencheAffichageEcran(int typeEcran) throws ExceptionVue {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.afficheEcran(typeEcran);
			}
		}
		
	}	

	/**
	 * Déclenche l'affichage d'un écran
	 * @param typeEcran Le type d'écran
	 * @param objet Objet nécessaire pour l'opération
	 * @throws ExceptionVue 
	 */
	public void declencheAffichageEcran(int typeEcran, Object objet) throws ExceptionVue {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.afficheEcran(typeEcran,objet);
			}
		}
		
	}

	/**
	 * Déclenche la mise à jour des menus dynamiques sessions
	 */
	public void declencheMAJMenus() {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.majMenus(this);
			}
		}
		
	}	

	/**
	 * Notifie une exception
	 */
	public void notifieException(Exception exception) {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.afficheException(exception);
			}
		}
		
	}
	
	/**
	 * Notifie d'un probleme de chargement de librairie
	 * @param typeException Le type d'exception rencontrée
	 */
	private void notifieProblemeMAJLibrairieP11(int typeException) {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
	
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.problemeChargementLibrairie(typeException, this);
			}
		}
		
	}
	
	/**
	 * Notifie les écouteurs de la mise à jour de la librairie
	 */
	public void notifieMAJLibrairieP11() {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.chargementLibrairie(new String(infoLibrairieP11.libraryDescription).trim());
			}
		}
		
	}	

	
	/**
	 * Déclenche la demande à l'utilisateur de connexion d'un lecteur
	 * @return Réponse utilisateur
	 */
	public int declencheDemandeConnexionLecteur() {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeConnexionLecteur();
			}
		}
		
		return 0;
		
	}

	/**
	 * Déclenche la demande à l'utilisateur d'insertion d'une carte supportee
	 * @return Réponse utilisateur
	 */
	public int declencheDemandeInsertionCarte() {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeInsertionCarte();
			}
		}
		
		return 0;
		
	}

	/**
	 * Déclenche la demande à l'utilisateur de saisie du code porteur
	 * 
	 * @param nbEssais Le type de saisie (à N tentatives, à 3 tentatives)
	 * @param conformiteCodePorteur Flag permettant de déterminer le message à afficher
	 * @param numeroSerieCarte Numéro de série de la carte
	 * @return Tableau de 2 éléments, contenant le choix utilisateur [0] et le code porteur saisi [1]
	 */
	public Object[] declencheDemandeSaisieCodePorteur(long nbEssais, long conformiteCodePorteur, String numeroSerieCarte) {

		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeSaisieCodePorteur(nbEssais, conformiteCodePorteur, numeroSerieCarte);
			}
		}
		
		return null;
	}	

	/**
	 * Déclenche la demande à l'utilisateur de recyclage du code porteur
	 * 
	 * @param nbEssais Flag permettant de déterminer le message à afficher concernant le nombre d'essai
	 * @param conformiteCodeDeblocage Etat de conformité du code de déblocage
	 * @param conformiteCodePorteur Etat de conformité du code porteur
	 * @param numeroSerieCarte Numéro de série de la carte
	 * @return Tableau de 4 éléments, contenant le choix utilisateur [0] et le code de déblocage [1], le 1er code porteur saisi [2] et le 2eme code porteur saisi [3]
	 */
	public Object[] declencheDemandeRecyclageCodePorteur(long nbEssais, long conformiteCodeDeblocage, long conformiteCodePorteur, String numeroSerieCarte) {

		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeRecyclageCodePorteur(nbEssais, conformiteCodeDeblocage, conformiteCodePorteur, numeroSerieCarte);
			}
		}
		
		return null;
	}
	
	
	
	/**
	 * Déclenche la demande de choix du nombre d'essais possibles avant blocage du code porteur
	 * @return Réponse utilisateur
	 */
	public int declencheDemandeChoixNbEssaisPossiblesAvantBlocageCodePorteur() {
		
		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeChoixNbEssaisPossiblesAvantBlocageCodePorteur();
			}
		}
		
		return 0;
	}

	/**
	 * Déclenche demande d'action lecteur (retrait/connexion) ou carte (retrait/insertion)
	 * 
	 * @param typeAction Le type d'action à demander
	 * @param numeroSerieCarte Numéro de série de la carte
	 * @return Réponse utilisateur
	 */
	public int declencheDemandeActionLecteurCarte(int typeAction, String numeroSerieCarte) {

		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.demandeActionLecteurCarte(typeAction, numeroSerieCarte);
			}
		}		
		
		return 0;
	}	

	/**
	 * Déclenche l'alerte de carte retrouvée
	 */	
	public void declencheAlerteCarteRetrouvee() {

		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				ecouteur.declencheAlerteCarteRetrouvee();
			}
		}
		
	}
	
	/**
	 * Déclenche la demande de modification d'un objet de données applicatives
	 * 
	 * @param valeur Valeur à modifier
	 * @return Tableau de 2 éléments, contenant le choix utilisateur [0] et le valeur de l'attribut modifiée [1]
	 */	
	public Object[] declencheDemandeModificationObjetDonneesApplicatives(byte[] valeur) {

		if(ecouteurs != null) {
			ApplicationEcouteur[] listeEcouteurs = (ApplicationEcouteur[])ecouteurs.getListeners(ApplicationEcouteur.class);
			
			for(ApplicationEcouteur ecouteur : listeEcouteurs) {
				return ecouteur.declencheDemandeModificationObjetDonneesApplicatives(valeur);
			}
		}
		
		return null;
	}
	
	
	
	
	/**
	 * Charge une librairie
	 * @throws ExceptionLibrairie
	 */
	private void chargerLibrairie() throws ExceptionLibrairie {
		
			// On tente de terminer la librairie
			if(librairieP11 != null)
				try { librairieP11.C_Finalize(null); } catch (Exception e){};

			try {
				// On charge la nouvelle librairie
				librairieP11 = PKCS11Connector.connectToPKCS11Module(this.cheminLibrairieP11);
			} catch(Throwable t) {

				// Si une erreur PKCS#11 est rencontrée on log et on lève l'exception
				log.error("Une erreur est survenue au chargement de la librairie PKCS#11 : "+t.getMessage());
				throw new ExceptionLibrairie(ExceptionProgrammeExemple.TYPE_LIBRAIRIE_EXCEPTION_CHARGEMENT_LIB_TS, "Une erreur est survenue au chargement de la librairie PKCS#11");		
				
			}
			
			
			try {
				// On charge la librairie native des 
				traitementsSpecifiques = ConnecteurJNI.connecteAuModulePKCS11(this, this.cheminLibrairieTS, this.cheminLibrairieP11); 
			} catch(Throwable t) {
				
				// Si une erreur PKCS#11 est rencontrée on log et on lève l'exception
				log.error("Une erreur est survenue au chargement de la librairie native des traitements spécifiques : "+t.getMessage());
				throw new ExceptionLibrairie(ExceptionProgrammeExemple.TYPE_LIBRAIRIE_EXCEPTION_CHARGEMENT_LIB_TS, "Une erreur est survenue au chargement de la librairie native des traitements spécifiques. Le module traitements spécifiques est inutilisable.");		
				
			}
			
	}	
	
	/**
	 * Charge la librairie PKCS#11 à utiliser
	 */
	private void chargerInfoLibrairie() {
		
		try {
			
			librairieP11.C_Initialize(null);
			infoLibrairieP11 = librairieP11.C_GetInfo();
			librairieP11.C_Finalize(null);
			
		} catch (PKCS11Exception e) {
			
			log.error("Une erreur est survenue au chargement des informations de la librairie PKCS11 : "+ExceptionProgrammeExemple.getStacktrace(e));
			
		}
		
	}

	/**
	 * Ajoute les slots spécifiés à l'application
	 * @param slots Slots à ajouter
	 * @param ajouteCartesPresentes Détermine s'il faut ajouter les cartes présentes
	 * @throws ExceptionObjet 
	 * @throws PKCS11Exception 
	 */
	public void ajouteLesSlots(Vector<Object> slots, boolean ajouteCartesPresentes) throws ExceptionObjet, PKCS11Exception {
		
		if(this.slots == null)
			this.slots = new Vector<Slot>();
		
		for (Iterator<Object> iterator = slots.iterator(); iterator.hasNext();) {
			
			long idSlot = (Long)iterator.next();
			Slot slot = new Slot(librairieP11,idSlot);
			this.slots.add(slot);
			
			// Si un token est present sur le slot, on prend en compte ce token
			if (ajouteCartesPresentes && (librairieP11.C_GetSlotInfo(idSlot).flags & PKCS11Constants.CKF_TOKEN_PRESENT) != 0) {
				
				if(tokens == null)
					tokens = new Vector<Token>();
				tokens.add(new Token(librairieP11, idSlot));
				
			}
		}
		
	}
	
	/**
	 * Ajoute les tokens (cartes) spécifiés à l'application
	 * @param tokens Tokens à ajouter
	 * @throws ExceptionObjet 
	 */
	public void ajouteLesTokens(Vector<Object> tokens) throws ExceptionObjet {
		
		for (Iterator<Object> iterator = tokens.iterator(); iterator.hasNext();) {
			
			Token token = new Token(librairieP11,(Long)iterator.next());
			
			if(this.tokens == null)
				this.tokens = new Vector<Token>();
			
			this.tokens.add(token);
			
		}

	}		

	/**
	 * Ajoute les algorithmes au token
	 * @param token sur lequel ajouter les algorithmes
	 * @param algos Algorithmes à ajouter
	 * @throws ExceptionObjet 
	 */
	public void ajouteLesAlgo(Token token, Vector<Object> algos) throws ExceptionObjet {
		
		for (Iterator<Object> iterator = algos.iterator(); iterator.hasNext();) {
			
			Algo algo = new Algo(librairieP11,token.getIdSlotToken(),(Long)iterator.next());
			
			if(this.algos == null)
				this.algos = new TreeMap<String, Vector<Algo>>();
			
			if(this.algos.get(token.getNumeroSerie()) == null)
				this.algos.put(token.getNumeroSerie(),new Vector<Algo>());
			
			this.algos.get(token.getNumeroSerie()).add(algo);
			
		}

	}		

	/**
	 * Ajoute les objets (clé, certificats...) du token
	 * @param librairie Librairie PKCS#11 à utiliser
	 * @param session La session
	 * @param idToken Numéro de série de la carte porteuse des objets
	 * @param objets Vecteur d'objets pouvant contenir des objets (clé, certificats...)
	 * @param type Type d'objet
	 * @throws ExceptionObjet 
	 */
	public void ajouteLesObjets(PKCS11 librairie, Session session, String idToken, Vector<Object> objets, int type) throws ExceptionObjet {
		
		Vector<Long> ajoutes = new Vector<Long>();
		
		for (Iterator<Object> iterator = objets.iterator(); iterator.hasNext();) {
			Object objet = iterator.next();
			
			// Ajout de cle(s)
			if(type == TraitementsUtil.TYPE_OBJET_CLE_PUBLIQUE) {
				
				long[] cle = (long[])objet;
				
				if(this.clesPubliques == null)
					this.clesPubliques = new TreeMap<String, Vector<Cle>>();
				
				if(this.clesPubliques.get(idToken) == null)
					this.clesPubliques.put(idToken,new Vector<Cle>());
				
				this.clesPubliques.get(idToken).add(new Cle(librairie,session.getIdSession(),cle[0],cle[1]));

				
			} else if(type == TraitementsUtil.TYPE_OBJET_CLE_PRIVE) {

				long[] cle = (long[])objet;
				
				if(this.clesPrivees == null)
					this.clesPrivees = new TreeMap<String, Vector<Cle>>();
					
				if(this.clesPrivees.get(idToken) == null)
					this.clesPrivees.put(idToken,new Vector<Cle>());

				this.clesPrivees.get(idToken).add(new Cle(librairie,session.getIdSession(),cle[0],cle[1]));

			}
			
			// Ajout des objets
			else if(type == TraitementsUtil.TYPE_OBJET_DONNEES_APPLICATIVES) {
				
				if(this.objets == null)
					this.objets = new TreeMap<String, Vector<Objet>>();

				if(this.objets.get(idToken) == null)
					this.objets.put(idToken,new Vector<Objet>());

				if(!ajoutes.contains((Long)objet)) {
					this.objets.get(idToken).add(new Objet(librairie,session.getIdSession(),(Long)objet));
					ajoutes.add((Long)objet);
				}
				
			}
		}
		
	}
	
	/**
	 * Ajoute la session à l'application
	 * @param session La session à ajouter
	 * @throws ExceptionObjet 
	 */
	public void ajouteLaSession(Token token, long session) throws ExceptionObjet {
		
		if(token != null && token.getNumeroSerie() != null) {
			
			Session maSession = new Session(librairieP11,token.getNumeroSerie(),session);
			
			if(sessions == null)
				sessions = new TreeMap<String, Vector<Session>>();
			
			if(sessions.get(token.getNumeroSerie()) == null)
					sessions.put(token.getNumeroSerie(), new Vector<Session>());
			
			sessions.get(token.getNumeroSerie()).add(maSession);

			sessionCourante = maSession;
			
		}
		
	}


	
	/**
	 * Ajoute un écouteur à notifier en cas d'évènement
	 * @param ecouteur Ecouteur à notifier en cas d'évènement
	 */
	public void ajouteApplicationEcouteur(ApplicationEcouteur ecouteur) {
		
		if(ecouteurs != null)
			ecouteurs.add(ApplicationEcouteur.class, ecouteur);
		
	}

	/**
	 * Supprime tous les éléments en cours (slots, tokens, algorithmes, session ...)
	 */
	public void supprimeTousLesElementsEnCours() {
		
		if(slots != null)
			slots.removeAllElements();
		if(tokens != null)
			tokens.removeAllElements();
		if(algos != null)
			algos.clear();
		if(sessions != null)
			sessions.clear();
		if(clesPubliques != null)
			clesPubliques.clear(); 
		if(clesPrivees != null)
			clesPrivees.clear();
		if(objets != null)
			objets.clear();
		
		sessionCourante = null;		
		signatureMessage = null;
		signatureFichier = null;
		messageSigne = null;
		fichierSigne = null;

	}	

	/**
	 * Supprime tous les algorithmes du token
	 * @param numeroSerieToken Numéro de série de la carte porteuse des algorithmes
	 */
	public void supprimeTousLesAlgo(String numeroSerieToken) {

		// Suppression des algorithmes
		if(algos != null && algos.get(numeroSerieToken) != null)
			algos.get(numeroSerieToken).removeAllElements();

	}

	/**
	 * Supprime un objet du menu
	 * @param objet Objet à supprimer
	 */
	public void supprimeObjet(String token, Objet objet) {
		
		if(token != null && objet != null && objets != null && objets.get(token) != null) {
			objets.get(token).remove(objet);
		}
		
	}
	
	/**
	 * Supprime tous les objets d'une carte
	 * @param numeroSerieToken Numéro de série de la carte porteuse des objets
	 */
	public void supprimeTousLesObjets(String numeroSerieToken) {

		// Suppression des clefs publiques
		supprimeTousLesObjetsDeType(numeroSerieToken, TraitementsUtil.TYPE_OBJET_CLE_PRIVE);
		// Suppression des clefs privees
		supprimeTousLesObjetsDeType(numeroSerieToken, TraitementsUtil.TYPE_OBJET_CLE_PUBLIQUE);
		// Suppression des objets
		supprimeTousLesObjetsDeType(numeroSerieToken, TraitementsUtil.TYPE_OBJET_DONNEES_APPLICATIVES);
		
	}	

	/**
	 * Supprime tous les objets d'un certain type d'une carte
	 * @param numeroSerieToken Numéro de série de la carte porteuse des objets
	 * @param typeObjet Type d'objet à supprimer
	 */
	public void supprimeTousLesObjetsDeType(String numeroSerieToken, int typeObjet) {

		// Suppression des clefs publiques
		if(typeObjet == TraitementsUtil.TYPE_OBJET_CLE_PUBLIQUE) {
			if(clesPubliques != null && clesPubliques.get(numeroSerieToken) != null)
				clesPubliques.get(numeroSerieToken).removeAllElements();
		}
		// Suppression des clefs privees
		else if(typeObjet == TraitementsUtil.TYPE_OBJET_CLE_PRIVE) {
			if(clesPrivees != null && clesPrivees.get(numeroSerieToken) != null)
				clesPrivees.get(numeroSerieToken).removeAllElements();
		}
		// Suppression des objets
		else  if(typeObjet == TraitementsUtil.TYPE_OBJET_DONNEES_APPLICATIVES) {
			if(objets != null && objets.get(numeroSerieToken) != null)
			objets.get(numeroSerieToken).removeAllElements();
		}
		
	}			

	/**
	 * Supprime une session du menu
	 * @param session La session à supprimer
	 */
	public void supprimeLaSession(String token, Session session) {
		
		if(token != null && session != null && token != null && sessions != null && sessions.get(token) != null) {
			
			sessions.get(token).remove(session);
			
			// Mise à jour de la session courante si nécessaire
			if(session == sessionCourante && sessions.get(token) != null && sessions.get(token).size() != 0)
				sessionCourante = sessions.get(token).get(0);
			
			else if(session == sessionCourante) {
				sessionCourante = null;
				for (Iterator<String> iterator = sessions.keySet().iterator(); iterator.hasNext() && sessionCourante == null;) {
					String numeroSerieToken = iterator.next();
					if(!numeroSerieToken.equalsIgnoreCase(token) && sessions.get(numeroSerieToken) != null && sessions.get(numeroSerieToken).size() != 0)
						sessionCourante = sessions.get(numeroSerieToken).get(0);
				}
			}
			
		}
		
	}	
	
	/**
	 * Supprime toutes les sessions d'une carte
	 * @param token 
	 * @throws ExceptionObjet
	 */
	public void supprimeToutesLesSessions(Token token) throws ExceptionObjet {
		
		if(sessions != null && token.getNumeroSerie() != null && sessions.get(token.getNumeroSerie()) != null)
			sessions.get(token.getNumeroSerie()).removeAllElements();

	}

	/**
	 * Compte le nombre de sessions actives
	 * @return Le nombre de sessions actives 
	 */
	public int nbSessionsActives() {
		
		int nbSessions = 0;
		
		if(sessions != null && sessions.values() != null) {
			
			for (Iterator<Vector<Session>> iterator = sessions.values().iterator(); iterator.hasNext();) {
				Vector<Session> vSessions = iterator.next();
				nbSessions = nbSessions + vSessions.size();
			}
			
		}
		
		return nbSessions;

	}
	
	/**
	 * Supprime un écouteur en attente d'évènement
	 * @param ecouteur Ecouteur en attente d'évènement
	 */
	public void supprimeApplicationEcouteur(ApplicationEcouteur ecouteur) {
		
		if(ecouteurs != null)
			ecouteurs.remove(ApplicationEcouteur.class, ecouteur);
	}

	/**
	 * Détermine le type de système d'exploitation utilisé
	 * @return Le type de système d'exploitation utilisé
	 */
	public static String getTypeOS() {
		
		String os = System.getProperty("os.name").toLowerCase();
		if(os.indexOf( "win" ) >= 0)
				return "w"+System.getProperty("sun.arch.data.model");
		else if(os.indexOf( "nix") >=0 || os.indexOf( "nux") >=0)
			return "lux";
		else if(os.indexOf( "mac" ) >= 0)
			return "mac";
		else
			return "";
				
	}
	
	/**
	 * Détermine le filtre à utiliser lors de la sélection d'une librairie
	 * @return Le filtre à utiliser lors de la sélection d'une librairie
	 */
	public static FileFilter getFiltreLibrairie() {	
	
		FileFilter filtreFichiers = new FileFilter() {
			
			@Override
			public String getDescription() {
				if(getTypeOS().equalsIgnoreCase("w32")
						|| Application.getTypeOS().equalsIgnoreCase("w64"))
					return "Fichiers .dll uniquement";
				else if(Application.getTypeOS().equalsIgnoreCase("lux"))
					return "Fichiers .so uniquement";
				else if(Application.getTypeOS().equalsIgnoreCase("mac"))
					return "Fichiers .dylib uniquement";
				else
					return null;
			}
			
			@Override
			public boolean accept(File f) {
				
				if (f.isDirectory())
					return true;
				
				String extension = null;
		        String s = f.getName();
		        int i = s.lastIndexOf('.');
	
		        if (i > 0 &&  i < s.length() - 1)
		        	extension = s.substring(i+1).toLowerCase();
	
			    if (extension != null) {
					if ((extension.equalsIgnoreCase("dll")
							&& (getTypeOS().equalsIgnoreCase("w32") || getTypeOS().equalsIgnoreCase("w64")))
					    || (extension.equalsIgnoreCase("so") && getTypeOS().equalsIgnoreCase("lux"))
					    || (extension.equalsIgnoreCase("dylib") && getTypeOS().equalsIgnoreCase("mac")))
						return true;
					else
						return false;
			    }
	
				return false;						
				
			}
		};
		
		return filtreFichiers;
		
	}
	
}
